﻿using housingsafety.server.Entities.DataModels;
using housingsafety.server.Helpers;
using housingsafety.server.Models;
using housingsafety.server.Repository;
using housingsafety.server.Security;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.IO;
using System.Threading.Tasks;

namespace housingsafety.server.Controllers
{
    /// <summary>
    /// 手机应用控制器
    /// </summary>
    [Authorize(policy: Policys.AdminOnly)]
    public class AppController : BaseController<AppController>
    {
        /// <summary>
        /// 更新信息文件
        /// </summary>
        private const string UpdateInfoFile = "update.json";
        private const string AppLatestFile = "app_latest.apk";
        private const string AppLatestBakFile = "app_latest.apk.bak";
        private const string UpdateLatestBakFile = "update.json.bak";

        /// <summary>
        /// 更新url base Path
        /// </summary>
        public const string UpdateUrlRootPath = "/app/files";

        public AppController(ILogger<AppController> logger
            , IRepository<ZmSystemLog> systemLogRepository) : base(logger, systemLogRepository)
        {

        }

        /// <summary>
        /// 查询APP更新信息
        /// </summary>
        /// <returns>APP更新信息</returns>
        [HttpGet("app-update/info")]
        [AllowAnonymous]
        [Produces("application/json")]
        [ProducesResponseType(typeof(RespRet<AppUpdateInfoDto>), 200)]
        public RespRet<AppUpdateInfoDto> CheckUpdate()
        {
            string basePath1 = EnsureAppsFolder();
            var f = Path.Combine(basePath1, UpdateInfoFile);
            if (!System.IO.File.Exists(f) || new FileInfo(f).Length < 1)
            {
                return new RespRet<AppUpdateInfoDto>(true, "Ok", new AppUpdateInfoDto(AppUpdateStatus.NoUpdate));
            }

            var fApp = Path.Combine(basePath1, AppLatestFile);
            if (!System.IO.File.Exists(fApp) || new FileInfo(fApp).Length < 1)
            {
                return new RespRet<AppUpdateInfoDto>(true, "Ok", new AppUpdateInfoDto(AppUpdateStatus.NoUpdate));
            }

            try
            {
                using (FileStream stream = new FileStream(f, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    using (var streamReader = new StreamReader(stream))
                    {
                        var jsonText = streamReader.ReadToEnd();

                        AppUpdateInfo appUpdateInfo = JsonConvert.DeserializeObject<AppUpdateInfo>(jsonText);

                        var url = $"{UpdateUrlRootPath}/{AppLatestFile}";
                        AppUpdateInfoDto ret = new AppUpdateInfoDto(appUpdateInfo);
                        //ret.AppUrl = url;
                        return new RespRet<AppUpdateInfoDto>(true, "Ok", ret);
                    }
                }
            }
            catch (Exception)
            {
                return new RespRet<AppUpdateInfoDto>(false, "read update info error!");
            }
        }

        /// <summary>
        /// 下载APP
        /// </summary>
        /// <returns></returns>
        [HttpGet("app/download")]
        [Produces("application/vnd.android.package-archive")]
        [ProducesResponseType(typeof(FileStreamResult), 200)]
        [AllowAnonymous]
        public async Task<IActionResult> Download()
        {
            string basePath1 = EnsureAppsFolder();
            var fApp = Path.Combine(basePath1, AppLatestFile);
            if (!System.IO.File.Exists(fApp) || new FileInfo(fApp).Length < 1)
            {
                return BadRequest(new RespRet(false, "App has not update"));
            }

            return await Task.FromResult(File(new FileStream(fApp, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
                , "application/vnd.android.package-archive"
                , $"app_lastest_{DateTime.Now:yyyyMMdd_HHmmss}.apk"));
        }

        /// <summary>
        /// 上传APP
        /// </summary>
        /// <param name="vm"></param>
        /// <returns></returns>
        [HttpPost("admin/app/upload")]
        [Produces("application/json")]
        [RequestSizeLimit(100_000_000)]
        [ProducesResponseType(typeof(RespRet), 200)]
        public async Task<RespRet> Upload([FromForm] AppDetailsViewModel vm)
        {
            if (!ModelState.IsValid)
            {
                return (new RespRet(false, "model state is valid"));
            }

            if (string.IsNullOrEmpty(vm.File.FileName))
            {
                return (new RespRet(false, "filename IsNullOrEmpty"));
            }

            if (!vm.File.FileName.EndsWith(".apk", StringComparison.OrdinalIgnoreCase))
            {
                return (new RespRet(false, "must apk file"));
            }

            string basePath1 = EnsureAppsFolder();
            string randomTmpName = $"app_upload_{DateTime.Now:yyyyMMdd_HHmmss}.tmp";
            var fTmp = Path.Combine(basePath1, randomTmpName);
            var fBak = Path.Combine(basePath1, AppLatestBakFile);

            using (var stream = new FileStream(fTmp, FileMode.Create))
            {
                await vm.File.CopyToAsync(stream);
            }

            var fJsonFile = Path.Combine(basePath1, UpdateInfoFile);

            try
            {
                var fJsonFileBak = Path.Combine(basePath1, UpdateLatestBakFile);
                if (System.IO.File.Exists(fJsonFile))
                {
                    //备份最新的
                    System.IO.File.Move(fJsonFile, fJsonFileBak, true);
                }

                var fApp = Path.Combine(basePath1, AppLatestFile);
                if (System.IO.File.Exists(fApp))
                {
                    //备份最新的
                    System.IO.File.Move(fApp, fBak, true);
                }

                System.IO.File.Move(fTmp, fApp, true);
                System.IO.File.Delete(fTmp);

                using (FileStream stream = new FileStream(fJsonFile, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite))
                {
                    using (var streamWriter = new StreamWriter(stream))
                    {
                        ApkReader.ApkReader apkReader = new ApkReader.ApkReader();
                        ApkReader.ApkInfo apkInfo = apkReader.Read(fApp);
                        using (FileStream apkStream = new FileStream(fApp, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                        {
                            AppUpdateInfo appUpdateInfo = new AppUpdateInfo();
                            appUpdateInfo.AppName = apkInfo.PackageName;
                            appUpdateInfo.AppVersion = apkInfo.VersionName;
                            appUpdateInfo.VersionId = apkInfo.VersionCode;
                            appUpdateInfo.Signature = apkStream.Md5();
                            appUpdateInfo.NecessaryUpdate = vm.NecessaryUpdate;
                            appUpdateInfo.Changelog = vm.Changelog;
                            appUpdateInfo.UploadTime = DateTime.Now;

                            var jsonText = JsonConvert.SerializeObject(appUpdateInfo, Formatting.Indented);
                            streamWriter.Write(jsonText);
                            await streamWriter.FlushAsync();

                            return new RespRet(true, "Ok");
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Logger.LogError($"Upload APP:{ex.Message}");
                return (new RespRet(false, "save file raise error"));
            }
        }

        /// <summary>
        /// app下载路径
        /// </summary>
        /// <returns></returns>
        public static string EnsureAppsFolder()
        {
            string basePath1 = AppContext.BaseDirectory;
            string uploadsFolder = Path.Combine(basePath1, "apps");
            Directory.CreateDirectory(uploadsFolder);
            return uploadsFolder;
        }
    }
}
